const jwt = require('jsonwebtoken');
const userService = require('./user.service');
const redis = require('redis');
const redisConfig = require('../config/redis.config');

// 创建Redis客户端
let redisClient;
let redisEnabled = true;
let reconnectAttempts = 0;

try {
  redisClient = redis.createClient({
    url: redisConfig.url,
    ...redisConfig.options
  });

  // 连接Redis
  (async () => {
    try {
      await redisClient.connect();
      
      // 选择指定的数据库
      if (redisConfig.database !== 0) {
        await redisClient.select(redisConfig.database);
        console.log(`Redis认证服务选择数据库: ${redisConfig.database}`);
      }
      
      console.log('Redis连接成功');
      
      // 添加重连成功的处理
      redisClient.on('connect', () => {
        console.log('Redis认证服务重新连接成功');
        redisEnabled = true;
        reconnectAttempts = 0;
        
        // 重新连接后需要重新选择数据库
        (async () => {
          try {
            if (redisClient.isReady && redisConfig.database !== 0) {
              await redisClient.select(redisConfig.database);
            }
          } catch (err) {
            console.error('Redis重连后选择数据库失败:', err.message);
          }
        })();
      });
    } catch (err) {
      console.error('Redis连接失败:', err.message);
      redisEnabled = false;
    }
  })();

  // 监听Redis连接错误
  redisClient.on('error', (err) => {
    console.error('Redis连接错误:', err.message);
    redisEnabled = false;
  });
  
  // 监听断开连接事件
  redisClient.on('end', () => {
    console.log('Redis连接已断开');
    redisEnabled = false;
  });
} catch (err) {
  console.error('Redis客户端创建失败:', err.message);
  redisEnabled = false;
}

// JWT密钥，实际应用中应从环境变量获取
const JWT_SECRET = process.env.JWT_SECRET;
const JWT_EXPIRES_IN = process.env.JWT_EXPIRES_IN;

/**
 * 认证服务层
 * 处理用户认证、令牌管理等功能
 */
class AuthService {
  /**
   * 用户登录
   * @param {string} username - 用户名
   * @param {string} password - 密码
   * @returns {Promise<Object>} 包含用户信息和令牌的对象
   */
  async login(username, password) {
    // 查找用户
    const user = await userService.findByUsername(username);
    
    if (!user) {
      throw new Error('用户不存在');
    }
    
    // 验证密码
    const isPasswordValid = userService.verifyPassword(password, user.password);
    
    if (!isPasswordValid) {
      throw new Error('密码错误');
    }
    
    // 检查用户状态
    if (user.status !== 'active') {
      throw new Error('账户已被禁用');
    }
    
    // 更新最后登录时间
    await userService.updateLastLogin(user.id);
    
    // 生成令牌
    const token = this.generateToken(user);
    
    // 存储令牌到Redis
    await this.storeToken(user.id, token);
    
    // 返回用户信息和令牌
    return {
      user: {
        id: user.id,
        username: user.username,
        email: user.email,
        avatar: user.avatar,
        role: user.role,
        status: user.status
      },
      token
    };
  }

  /**
   * 用户注册
   * @param {Object} userData - 用户数据
   * @returns {Promise<Object>} 包含用户信息和令牌的对象
   */
  async register(userData) {
    // 检查用户名是否已存在
    const existingUsername = await userService.findByUsername(userData.username);
    if (existingUsername) {
      throw new Error('用户名已被使用');
    }
    
    // 检查邮箱是否已存在
    const existingEmail = await userService.findByEmail(userData.email);
    if (existingEmail) {
      throw new Error('邮箱已被使用');
    }
    
    // 创建新用户
    const user = await userService.create(userData);
    
    // 生成令牌
    const token = this.generateToken(user);
    
    // 存储令牌到Redis
    await this.storeToken(user.id, token);
    
    // 返回用户信息和令牌
    return {
      user: {
        id: user.id,
        username: user.username,
        email: user.email,
        avatar: user.avatar,
        role: user.role,
        status: user.status
      },
      token
    };
  }

  /**
   * 用户登出
   * @param {number} userId - 用户ID
   * @returns {Promise<boolean>} 登出结果
   */
  async logout(userId) {
    if (!redisEnabled || !redisClient.isReady) {
      console.log('Redis不可用，跳过令牌删除');
      return true;
    }
    
    try {
      // 从Redis中删除令牌
      await redisClient.del(`user:${userId}:token`);
      return true;
    } catch (error) {
      console.error('删除令牌失败:', error.message);
      return true; // 即使失败也返回成功，不影响用户体验
    }
  }

  /**
   * 验证令牌
   * @param {string} token - JWT令牌
   * @returns {Promise<Object>} 解码后的令牌数据
   */
  async verifyToken(token) {
    try {
      // 解码令牌
      const decoded = jwt.verify(token, JWT_SECRET);
      
      // 如果Redis不可用，只验证JWT签名
      if (!redisEnabled || !redisClient.isReady) {
        console.log('Redis不可用，仅验证JWT签名');
        return decoded;
      }
      
      try {
        // 从Redis获取存储的令牌
        const storedToken = await redisClient.get(`user:${decoded.id}:token`);
        
        // 检查令牌是否存在于Redis中
        if (!storedToken || storedToken !== token) {
          throw new Error('令牌已失效');
        }
      } catch (redisError) {
        console.error('验证令牌时Redis操作失败:', redisError.message);
        // Redis错误时，仍然接受有效的JWT
      }
      
      return decoded;
    } catch (error) {
      throw new Error('无效的令牌');
    }
  }

  /**
   * 刷新令牌
   * @param {string} token - 当前令牌
   * @returns {Promise<Object>} 包含新令牌的对象
   */
  async refreshToken(token) {
    try {
      // 验证当前令牌
      const decoded = await this.verifyToken(token);
      
      // 获取用户信息
      const user = await userService.findById(decoded.id);
      
      if (!user) {
        throw new Error('用户不存在');
      }
      
      // 生成新令牌
      const newToken = this.generateToken(user);
      
      // 更新Redis中的令牌
      await this.storeToken(user.id, newToken);
      
      return { token: newToken };
    } catch (error) {
      throw new Error('刷新令牌失败');
    }
  }

  /**
   * 生成JWT令牌
   * @param {Object} user - 用户对象
   * @returns {string} JWT令牌
   */
  generateToken(user) {
    const payload = {
      id: user.id,
      username: user.username,
      email: user.email,
      role: user.role
    };
    
    return jwt.sign(payload, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN });
  }

  /**
   * 存储令牌到Redis
   * @param {number} userId - 用户ID
   * @param {string} token - JWT令牌
   * @returns {Promise<void>}
   */
  async storeToken(userId, token) {
    if (!redisEnabled || !redisClient.isReady) {
      console.log('Redis不可用，跳过令牌存储');
      return;
    }
    
    try {
      // 解析JWT过期时间
      const decoded = jwt.decode(token);
      const expiresIn = decoded.exp - Math.floor(Date.now() / 1000);
      
      // 存储令牌到Redis，设置相同的过期时间
      await redisClient.set(`user:${userId}:token`, token, {
        EX: expiresIn
      });
    } catch (error) {
      console.error('存储令牌到Redis失败:', error.message);
      // 错误不会影响主流程
    }
  }
}

module.exports = new AuthService();